package com.lgl.study.annotation;

import com.alibaba.druid.util.StringUtils;
import com.lgl.study.factory.ProxyFactory;

import java.io.File;
import java.io.FileFilter;
import java.io.IOException;
import java.lang.annotation.Annotation;
import java.lang.reflect.Field;
import java.net.URL;
import java.net.URLDecoder;
import java.util.ArrayList;
import java.util.Enumeration;
import java.util.List;
import java.util.concurrent.ConcurrentHashMap;

/**
 * @Auther: Brook
 * @Date: 2020/9/1 23:07
 * @Description: 手写@Service @Autowire  @Transaction
 */
public class AnnotationApplicationContext {
    private String packageName;
    ConcurrentHashMap<String, Object> beans = new ConcurrentHashMap<>();

    public AnnotationApplicationContext(String packageName) {
        this.packageName = packageName;
        this.initBeans();
    }

    private void initBeans() {
        try {
            List<Class<?>> allClasses = this.getAllClasses(this.packageName);
            this.instanceAnnotationClass(allClasses);
        } catch (Exception e) {

        }
    }

    public Object getBean(String beanName) {
        Object bean = beans.get(beanName);
        if (bean == null) {
            System.out.println("初始化bean失败，没有成功创建实例");
        }

        this.setBeanProperty(bean);

        Class aClass = bean.getClass();
        Annotation annotation = aClass.getAnnotation(Transactional.class);
        if (annotation != null) {
            ProxyFactory proxyFactory = (ProxyFactory) this.getBean("proxyFactory");
            return proxyFactory.getCglibProxy(bean);
        }

        return beans.get(beanName);
    }

    /**
     * 配置Bean的属性，实现@Autowire注解
     * @param beanObject
     */
    private Object setBeanProperty(Object beanObject) {
        Class beanClass = beanObject.getClass();
        Field[] declareFields = beanClass.getDeclaredFields();

        try {
            for (Field field : declareFields) {
                if (!field.isAccessible()) {
                    field.setAccessible(true);
                }
                Autowire autowire = field.getAnnotation(Autowire.class);
                if (autowire == null) {
                    break;
                }

                Class fieldClass = field.getType();
                String fieldObjectBeanId = this.getBeanId(fieldClass, "");
                Object fieldObject = beans.get(fieldObjectBeanId);
                Object object = setBeanProperty(fieldObject);
                field.set(beanObject, object);

            }
        } catch (Exception e) {

        }


        return beanObject;
    }


    /**
     * 实现@Service注解
     * @param classes
     * @return
     */
    private void instanceAnnotationClass(List<Class<?>> classes)
        throws IllegalAccessException, InstantiationException {
        for (Class<?> classInfo : classes) {
            Service serviceAnnotation = classInfo.getAnnotation(Service.class);
            if (serviceAnnotation != null) {
                String className = this.getBeanId(classInfo, serviceAnnotation.value());

                String beanId = toLowerFirstWord(className);
                Object bean = classInfo.newInstance();
                if (beans.get(beanId) == null) {
                    beans.put(beanId, bean);
                }
            }
        }
    }

    private String  getBeanId(Class<?> aClass, String defId) {
        if (!StringUtils.isEmpty(defId)) {
            return  defId;
        }
        Class<?>[] interfaces = aClass.getInterfaces();
        if (interfaces.length > 0) {
            Class<?> interfaceClass = interfaces[0];
            return this.toLowerFirstWord(interfaceClass.getSimpleName());
        }

        return this.toLowerFirstWord(aClass.getSimpleName());
    }

    /**
     * beanId 首字母要小写
     * @param className
     * @return
     */
    private String toLowerFirstWord(String className) {
        if (StringUtils.isEmpty(className)) {
            return "";
        }

        if (Character.isLowerCase(className.charAt(0))) {
            return className;
        } else {
            return (new StringBuilder()).append(Character.toLowerCase(className.charAt(0)))
                .append(className.substring(1)).toString();
        }
    }

    /**
     * 获取指定包名下的所有类
     * @param packageName
     * @return
     */
    private List<Class<?>> getAllClasses(String packageName) {
        List<Class<?>> classes = new ArrayList<>();
        String packageDirName = packageName.replace('.', '/');
        Enumeration<URL> dirs;
        try {
            dirs = Thread.currentThread().getContextClassLoader().getResources(packageDirName);
            while (dirs.hasMoreElements()) {
                URL url = dirs.nextElement();
                String protocol = url.getProtocol();
                if (protocol.equals("file")) {
                    String filePath = URLDecoder.decode(url.getFile(), "UTF-8");
                    findAndAddClassesInPackageByFilePath(packageName, filePath, classes);
                }
            }
        } catch (IOException | ClassNotFoundException ioException) {

        }
        return classes;
    }

    private void findAndAddClassesInPackageByFilePath(String packageName, String filePath, List<Class<?>> classList)
        throws ClassNotFoundException {
        File dir = new File(filePath);
        File[] files = dir.listFiles(new FileFilter() {
            @Override public boolean accept(File file) {
                return file.isDirectory() || file.getName().endsWith(".class");
            }
        });

        for (File file : files) {
            if (file.isDirectory()) {
                findAndAddClassesInPackageByFilePath(packageName + "." + file.getName(),
                    file.getAbsolutePath(), classList);
            } else {
                //去掉文件后缀
                String className = file.getName().substring(0, file.getName().length() - 6);
                classList.add(Class.forName(packageName + "." + className));
            }
        }
    }
}
